package logic

import (
	"bytes"
	"github.com/alecthomas/chroma/formatters/html"
	toc "github.com/mdigger/goldmark-toc"
	"github.com/yuin/goldmark"
	highlighting "github.com/yuin/goldmark-highlighting"
	"github.com/yuin/goldmark/extension"
	"go.uber.org/zap"
	"strconv"
	"struggle/dao/mysql"
	"struggle/model"
	"struggle/pkg/snowflake"
)

// CreateQuestion 创建问题
func CreateQuestion(question *model.Question) (err error) {
	// 防止 XSS 攻击 --> `html/template` 内容检测和转义
	// 敏感词过滤 --> 涉黄、涉恐等屏蔽词
	qid, err := snowflake.GenID()
	if err != nil {
		zap.L().Error("generate question id failed", zap.Error(err))
		return
	}
	question.QuestionID = qid
	err = mysql.CreateQuestion(question)
	if err != nil {
		zap.L().Error("mysql.CreateQuestion failed", zap.Error(err))
		return
	}
	return
}

func GetQuestionList(offset, limit int) (apiQuestionList *model.ApiQuestionList, err error) {
	questionList, err := mysql.GetQuestionList(offset, limit)
	if err != nil {
		zap.L().Error("mysql.GetQuestionList failed", zap.Error(err))
		return
	}
	//questionList := *questionInfoList
	if len(questionList) == 0 {
		zap.L().Info("question list is null")
		return
	}

	var userIDList []uint64
	for _, v := range questionList {
		userIDList = append(userIDList, v.AuthorID)
	}
	userInfoList, err := mysql.GetUserInfoList(userIDList)
	if err != nil {
		zap.L().Error("mysql.GetUserInfoList failed", zap.Any("userIDList", userIDList), zap.Error(err))
		return
	}
	zap.L().Debug("GetQuestionList", zap.Any("questionList", questionList), zap.Any("userInfoList", userInfoList))
	// 涉及到大量数据的 slice, 如果知道数据的数量，应当使用 make 预先初始化, 比 append() 扩容效率更高
	apiQuestionList = &model.ApiQuestionList{
		QuestionList: make([]*model.ApiQuestion, 0, len(questionList)),
	}

	for _, v := range questionList {
		apiQuestion := &model.ApiQuestion{
			QuestionInfo: *v,
		}
		// 问题的作者
		for _, user := range userInfoList {
			if user.UserSn == v.AuthorID {
				apiQuestion.AuthorName = user.Email
				break
			}
		}
		// 问题的回答数量
		count, err := mysql.GetAnswerCount(apiQuestion.QuestionID)
		if err != nil {
			zap.L().Error("mysql.GetAnswerCount failed", zap.Uint64("questionID", apiQuestion.QuestionID), zap.Error(err))
		}
		apiQuestion.AnswerCount = count
		// 问题最近回答的作者和时间
		answer, err := mysql.GetAnswerLately(apiQuestion.QuestionID)
		if err != nil {
			zap.L().Error("mysql.GetAnswerLately failed", zap.Uint64("questionID", apiQuestion.QuestionID), zap.Error(err))
		}
		user, err := mysql.GetUserByID(strconv.Itoa(int(answer.AuthorID)))
		if err != nil {
			zap.L().Error("mysql.GetUserByID failed", zap.Uint64("answer.AuthorID", answer.AuthorID), zap.Error(err))
		}
		apiQuestion.AnswerAuthor = user.Email
		apiQuestion.AnswerTime = answer.CreateTime
		apiQuestion.AnswerUpdateAt = answer.UpdateTime
		apiQuestionList.QuestionList = append(apiQuestionList.QuestionList, apiQuestion)
	}
	count, err := mysql.GetQuestionCount()
	if err != nil {
		zap.L().Error("mysql.GetQuestionCount failed", zap.Error(err))
		return
	}
	apiQuestionList.TotalCount = count
	return
}

// GetQuestionDetail 获取问题详情
func GetQuestionDetail(questionID uint64) (data *model.ApiQuestionDetail, err error) {
	var question *model.Question
	question, err = mysql.GetQuestion(questionID)
	if err != nil {
		zap.L().Error("get question failed", zap.Error(err))
		return
	}

	// 自定义解析器
	markdown := goldmark.New(
		goldmark.WithExtensions(extension.GFM), // 支持 GFM
		goldmark.WithExtensions( // 语法高亮插件
			highlighting.NewHighlighting(
				highlighting.WithStyle("monokai"),
				highlighting.WithFormatOptions(
					html.WithLineNumbers(true)))),
	)
	var buf bytes.Buffer
	convertFunc := toc.Markdown(markdown)
	_, err = convertFunc([]byte(question.Content), &buf)
	if err != nil {
		zap.L().Error("服务端解析 Markdown 错误", zap.Error(err))
		return nil, err
	}
	question.HTML = buf.String()
	question.QuestionID = questionID

	//categoryMap, err := mysql.MGetCategory([]int32{question.CategoryID})
	//if err != nil {
	//	zap.L().Error("get category failed", zap.Error(err))
	//	return
	//}
	//
	//category, ok := categoryMap[question.CategoryID]
	//if !ok {
	//	zap.L().Error("get category failed", zap.Error(err))
	//	return
	//}

	user, err := mysql.GetUserByID(strconv.Itoa(int(question.AuthorID)))
	if err != nil {
		zap.L().Error("get user info failed", zap.Error(err))
		return
	}

	apiQuestionDetail := &model.ApiQuestionDetail{}
	apiQuestionDetail.Question = *question
	apiQuestionDetail.AuthorName = user.Email
	//apiQuestionDetail.CategoryName = category.CategoryName
	return apiQuestionDetail, nil
}

// 获取个人用户问题列表
func GetUserQuestionList(sn uint64, offset, limit int) (apiQuestionList *model.QuestionList, err error) {
	questionList, err := mysql.DefaultUserCount.FindQuestions(sn, offset, limit)
	if err != nil {
		zap.L().Error("mysql.GetUserQuestionList failed", zap.Error(err))
		return
	}
	if len(questionList) == 0 {
		zap.L().Info("question list is null")
		return
	}

	count, err := mysql.GetUserQuestionCount(sn)
	if err != nil {
		zap.L().Error("mysql.GetUserQuestionCount failed", zap.Error(err))
		return
	}
	apiQuestionList = &model.QuestionList{
		QuestionList: make([]*model.Question, 0, len(questionList)),
	}
	apiQuestionList.QuestionList = questionList
	apiQuestionList.TotalCount = count
	return
}

type questionLogic struct{}

var DefaultQuestion = questionLogic{}

// 查询单个问题的扩展数据
func (q questionLogic) FindEx(sn uint64) (questionEx *model.QuestionEx, err error) {
	questionEx, err = mysql.DefaultQuestion.FindEx(sn)
	if err != nil {
		zap.L().Error("mysql.DefaultQuestion FindEx failed", zap.Error(err))
		return
	}
	return
}
